package com.credithc.signature.common.utils;

import com.credithc.signature.common.annotation.EndTime;
import com.credithc.signature.common.annotation.LikeSearch;
import com.credithc.signature.common.annotation.StartTime;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.RowBounds;
import tk.mybatis.mapper.entity.Example;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class ManagerBeanUtil {

    //-----------------------------常用字段类型----------------------------------
    private static final String STRINGTYPE = "class java.lang.String";
    private static final String INTEGERTYPE = "class java.lang.Integer";
    private static final String SHORTTYPE = "class java.lang.Short";
    private static final String DOUBLETYPE = "class java.lang.Double";
    private static final String LONGTYPE = "class java.lang.Long";
    private static final String BOOLEANTYPE = "class java.lang.Boolean";
    private static final String DATETYPE = "class java.util.Date";
    private static final String QUERYTIMEFILED = "createTime";
    private static final HashMap<String, String> typeMap = new HashMap<>(16);
    //-----------------------------常用其他字段----------------------------------
    private static final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");


    static {
        typeMap.put("STRINGTYPE", STRINGTYPE);
        typeMap.put("INTEGERTYPE", INTEGERTYPE);
        typeMap.put("SHORTTYPE", SHORTTYPE);
        typeMap.put("DOUBLETYPE", DOUBLETYPE);
        typeMap.put("LONGTYPE", LONGTYPE);
        typeMap.put("BOOLEANTYPE", BOOLEANTYPE);
        typeMap.put("DATETYPE", DATETYPE);
    }

    //------------------------------------------------1.反射封装请求参数-------------------------------------------------

    /**
     * 当Ro为空时
     *
     * @param var1
     * @return
     */
    public static Map<String, Object> buildQueryParams(Object var1) {
        return buildQueryParams(null, var1);
    }

    /**
     * 封装查询参数
     *
     * @return
     */
    public static Map<String, Object> buildQueryParams(Object var1, Object var2) {
        HashMap<String, Object> hashMap = new HashMap<>(16);
        //ro不为空
        if (var1 != null) {
            buildRoFields(var1, var2, hashMap);
        } else { //po不为空
            buildPoFields(var2, hashMap);
        }
        return hashMap;
    }

    /**
     * 封装Po属性
     *
     * @param var2
     * @param hashMap
     */
    private static void buildPoFields(Object var2, HashMap<String, Object> hashMap) {
        //获取Po的Class
        Class<?> aClassPo = var2.getClass();
        //1.创建Example类
        Example example = new Example(aClassPo);
        Example.Criteria criteria = example.createCriteria();
        //2.获取所有属性
        Field[] fields = aClassPo.getDeclaredFields();
        for (Field field : fields) {
            //3.获取每个属性的名字
            String name = field.getName();
            //4.获取属性的类型
            String type = field.getGenericType().toString();
            Object value = getValue(type, aClassPo, var2, name);
            if (value != null && value != "") {
                //判断Po中是否有该属性
                if (containsProp(aClassPo, name)) {
                    buildCriteria(field, type, criteria, name, value);
                }
            }
        }
        hashMap.put("example", example);
    }

    /**
     * 封装Ro属性
     *
     * @param var1
     * @param var2
     * @param hashMap
     */
    private static void buildRoFields(Object var1, Object var2, HashMap<String, Object> hashMap) {
        Integer pageNo = 0;
        Integer pageSize = 0;
        //获取Ro的Class
        Class<?> aClass = var1.getClass();
        //获取Po的Class
        Class<?> aClassPo = var2.getClass();
        //1.创建Example类
        Example example = new Example(aClassPo);
        Example.Criteria criteria = example.createCriteria();
        //2.获取所有属性
        Field[] fields = aClass.getDeclaredFields();
        for (Field field : fields) {
            //3.获取每个属性的名字
            String name = field.getName();
            //4.获取属性的类型
            String type = field.getGenericType().toString();
            if (name.equals("pageNo")) {
                pageNo = (Integer) getValue(type, aClass, var1, name);
                continue;
            }
            if (name.equals("pageSize")) {
                pageSize = (Integer) getValue(type, aClass, var1, name);
                continue;
            }
            Object value = getValue(type, aClass, var1, name);
            if (value != null && value != "") {
                //判断Po中是否有该属性
                if (containsProp(aClassPo, name)) {
                    buildCriteria(field, type, criteria, name, value);
                }
            }
        }
        hashMap.put("example", example);
        hashMap.put("criteria", criteria);
        //创建RowBounds类
        RowBounds rowBounds = new RowBounds(pageSize * (pageNo - 1), pageSize);
        hashMap.put("rowBounds", rowBounds);
    }


    private static Object getValue(String type, Class<?> aClass, Object var, String name) {
        //将属性的首字符大写，方便构造get，set方法
        name = name.substring(0, 1).toUpperCase() + name.substring(1);
        if (typeMap.containsValue(type)) {
            return execute(aClass, var, name);
        }
        return null;
    }

    /**
     * Criteria类 （需要按实际需求完善）
     *
     * @param field
     * @param type
     * @param criteria
     * @param name
     * @param value
     */
    private static void buildCriteria(Field field, String type, Example.Criteria criteria, String name, Object value) {
        //判断是否是日期
        if ("class java.util.Date".equals(type)) {
            if (field.isAnnotationPresent(StartTime.class)) {
                //判断是否为起始时间
                criteria.andGreaterThanOrEqualTo(QUERYTIMEFILED, value);
            } else if (field.isAnnotationPresent(EndTime.class)) {
                //判断是否为结束时间
                criteria.andLessThanOrEqualTo(QUERYTIMEFILED, value);
            } else {
                criteria.andEqualTo(name, value);
            }
        } else if ("class java.lang.String".equals(type)) {
            //判断是否为模糊查询
            if (field.isAnnotationPresent(LikeSearch.class)) {
                criteria.andLike(name, "%" + value + "%");
            } else {
                criteria.andEqualTo(name, value);
            }
        } else {
            criteria.andEqualTo(name, value);
        }
    }

    /**
     * 判断Po中是否有该属性
     *
     * @param aClassPo
     * @param name
     * @return
     */
    private static boolean containsProp(Class<?> aClassPo, String name) {
        Field[] fields = aClassPo.getDeclaredFields();
        for (Field field : fields) {
            if (field.getName().equals(name)) {
                return true;
            }
        }
        return false;
    }


    /**
     * 通过属性名获取属性值
     *
     * @param aClass
     * @param var
     * @param name
     * @return
     */
    private static Object execute(Class<?> aClass, Object var, String name) {
        try {
            //获得getter方法
            Method m = aClass.getMethod("get" + name);
            return m.invoke(var);
        } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            log.error("通过属性名获取对象属性值时发生异常", e);
        }
        return null;
    }

    //----------------------------------------------2.封装页面响应参数(时间)---------------------------------------------

    public static Date dateFormate(Date dateTime) {
        LocalDateTime localDateTime = convertDateToLDT(dateTime);
        String formatTime = formatter.format(localDateTime);
        LocalDateTime localDateTime1 = parseStringTime(formatTime);
        return convertLDTToDate(localDateTime1);
    }

    /**
     * Date转换为LocalDateTime
     *
     * @param date
     * @return
     */
    private static LocalDateTime convertDateToLDT(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), ZoneId.systemDefault());
    }

    /**
     * LocalDateTime转换为Date
     *
     * @param time
     * @return
     */
    private static Date convertLDTToDate(LocalDateTime time) {
        return Date.from(time.atZone(ZoneId.systemDefault()).toInstant());
    }

    /**
     * String转换为LocalDateTime
     *
     * @param dateNow
     * @return
     */
    private static LocalDateTime parseStringTime(String dateNow) {
        return LocalDateTime.parse(dateNow, formatter);
    }

    /**
     * 计算两个时间的时间查
     *
     * @param date1
     * @param date2
     * @return
     */
    public static long timeDifference(Date date1, Date date2) {
        Instant instant1 = date1.toInstant();
        Instant instant2 = date2.toInstant();
        return Duration.between(instant1, instant2).getSeconds();
    }


}
